# -*- coding: utf-8 -*-

"""剑指 Offer II 074. 合并区间
以数组 intervals 表示若干个区间的集合，其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间，并返回一个不重叠的区间数组，该数组需恰好覆盖输入中的所有区间。

示例 1：
输入：intervals = [[1,3],[2,6],[8,10],[15,18]]
输出：[[1,6],[8,10],[15,18]]
解释：区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

示例 2：
输入：intervals = [[1,4],[4,5]]
输出：[[1,5]]
解释：区间 [1,4] 和 [4,5] 可被视为重叠区间。

提示：
1 <= intervals.length <= 10^4
intervals[i].length == 2
0 <= starti <= endi <= 10^4"""


from sortedcontainers import SortedList

class Solution:
    """方案一，1.将区间集合排序；2.迭代合并
    方案二，采用类似插入排序的方案，维系一个合并排序好的集合，选择一个待合并区间，合并插入结果集合。这个集合需要支持频繁插入，按值快速查找，还要有序。
            那就用二叉查找树（红黑树），现成的实现为 sortedcontainers.SortedList，看文档 SortedList 为 order statistic tree（也即红黑树）；
            拿到一个区间去合并时，类似于打连接，
            answer: [[  ]  [   ]  [     ] [   ]]
            intervel:        [              ]
                          start勾右
                                          end勾左"""
    def merge(self, intervals):
        def bisect_GE_mini(start):
            left, right = 0, len(answer)-1
            if answer[left][1] >= start:
                return 0
            if answer[right][1] < start:
                return None
            while right-left > 1:
                midd = (left+right)//2
                if answer[midd][1] >= start:
                    right = midd
                else:
                    left = midd
            return right

        def bisect_LE_maxi(end):
            left, right = 0, len(answer)-1
            if answer[right][0] <= end:
                return right
            if answer[left][0] > end:
                return None
            while right-left > 1:
                midd = (left+right)//2
                if answer[midd][0] <= end:
                    left = midd
                else:
                    right = midd
            return left

        if len(intervals) == 1:
            return intervals
        
        answer = SortedList([intervals[0],])
        for interval in intervals[1:]:
            start, end = interval[0], interval[1]
            _left = bisect_GE_mini(start)
            _right = bisect_LE_maxi(end)
            # print('interval:%s, _left:%s, _right:%s' % (interval, _left, _right))

            if (_left is None) or (_right is None):
                answer.add(interval)
            elif _left > _right:
                answer.add(interval)
            elif _left <= _right:
                new_interval = [min(start, answer[_left][0]), max(end, answer[_right][1])]
                del_poi, del_count, del_total = _left, 0, _right-_left+1
                while del_count < del_total:
                    del answer[del_poi]
                    del_count += 1
                answer.add(new_interval)
        
        return list(answer)


if __name__ == '__main__':
    so = Solution()
    print(so.merge([[1,3],[2,6],[8,10],[15,18]]))
    print(so.merge([[1,4],[4,5]]))

